home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ident / tools / tcplist-1.1.shar / tcplist.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-14  |  19.6 KB  |  864 lines

  1. /*
  2.  * tcplist.c - make list of tcp connections, making use of rfc931 services
  3.  *             if available. 
  4.  *             
  5.  *             tcplist attempts to be efficient about rfc931 queries, using
  6.  *             the following techniques:
  7.  *             - all connections to a particular host are grouped together.
  8.  *             - connections to the rfc931 service on different hosts are 
  9.  *               made simultaneously, where available file descriptors allow,
  10.  *               using non-blocking I/O.
  11.  *             - if the rfc931 service on a host is not available, no further
  12.  *               requests will be made to that host.
  13.  *             - if the rfc931 service supports multiple queries per service
  14.  *               connection, tcplist will take advantage of this. 
  15.  * 
  16.  * Author: John DiMarco, University of Toronto, CDF
  17.  *         jdd@cdf.toronto.edu
  18.  */
  19.  
  20. #ifndef lint
  21. static char rcsid[] = "$Header: /u/jdd/src/jdd/distrib/tcplist/RCS/tcplist.c,v 1.9 1993/05/26 00:15:17 jdd Exp $";
  22. #endif
  23.  
  24. #include "patchlevel.h"
  25.  
  26. /* 
  27.  * $Log: tcplist.c,v $
  28.  * Revision 1.9  1993/05/26  00:15:17  jdd
  29.  * Release 1.1
  30.  *
  31.  * Revision 1.9  1993/05/26  00:09:08  jdd
  32.  * Release 1.1
  33.  *
  34.  * Revision 1.8  1993/05/25  18:07:33  jdd
  35.  * misc fixes
  36.  * Added -f option, to allow specifying a file with hosts not to rfc931-query.
  37.  * In an environment with many X-terminals and a limited number of file
  38.  * descriptors, this can dramatically speed up tcplist by reducing the
  39.  * number of rfc931 query passes that are required.
  40.  *
  41.  * Revision 1.7  1993/05/20  01:07:52  jdd
  42.  * Release 1.0
  43.  *
  44.  * Revision 1.6  1993/05/19  22:31:22  jdd
  45.  * didn't check for failed getpwuid. Fixed.
  46.  *
  47.  * Revision 1.4  1993/05/17  23:01:08  jdd
  48.  * misc fixes
  49.  *
  50.  * Revision 1.3  1992/10/16  19:50:04  jdd
  51.  * Port to IRIX 4.x
  52.  * Reworked rfc931 section to handle running out of file descriptors.
  53.  *
  54.  * Revision 1.3  1992/10/16  19:46:17  jdd
  55.  * port to IRIX
  56.  *
  57.  * Revision 1.2  1992/10/09  21:48:56  jdd
  58.  * complete rewrite of rfc931 stuff to use non-blocking I/O.
  59.  * 30 secs max timeout for all rfc931 calls, and all done in parallel,
  60.  * to improve performance.
  61.  *
  62.  * Revision 1.1  1992/07/30  00:10:24  jdd
  63.  * Initial revision
  64.  *
  65. # Revision 1.1  1991/08/15  23:24:10  jdd
  66. # Initial revision
  67. #
  68.  */
  69.  
  70. #define PORT_ANY 0
  71.  
  72. #include <stdio.h>
  73. #include <errno.h>
  74. #include <ctype.h>
  75. #include <nlist.h>
  76. #include <pwd.h>
  77. #include <signal.h>
  78. #include <syslog.h>
  79.  
  80. #ifdef HAVE_KVM
  81. #include <kvm.h>
  82. #else
  83. #include "kvm.h"
  84. #endif
  85.  
  86. #include <sys/types.h>
  87. #include <sys/stat.h>
  88. #include <sys/param.h>
  89. #include <sys/ioctl.h>
  90. #include <sys/socket.h>
  91. #include <sys/protosw.h>
  92. #include <sys/domain.h>
  93. #include <sys/socketvar.h>
  94.  
  95. #ifdef IRIX
  96. #define _KERNEL
  97. #else
  98. #define KERNEL
  99. #endif
  100.  
  101. #include <sys/file.h>
  102.  
  103. #if defined(IRIX) || defined(MIPS)
  104. #include <sys/inode.h>
  105. #include <sys/fstyp.h>
  106. #include <sys/fsid.h>
  107. #endif
  108.  
  109. #include <fcntl.h>
  110. #include <sys/user.h>
  111. #include <sys/wait.h>
  112.   
  113.  
  114. #ifdef IRIX
  115. #undef _KERNEL
  116. #else
  117. #undef KERNEL
  118. #endif
  119.  
  120.  
  121. #include <netdb.h>
  122. #include <net/if.h>
  123. #include <net/route.h>
  124. #include <netinet/in.h>
  125. #include <netinet/in_pcb.h>
  126. #include <netinet/tcp.h>
  127. #include <netinet/ip_var.h>
  128. #include <netinet/tcp_timer.h>
  129. #include <netinet/tcp_var.h>
  130. #include <arpa/inet.h>
  131.  
  132. #ifdef MIPS
  133. #include <sys/var.h>
  134. extern int errno;
  135. #endif
  136.  
  137. #include "utils.h"        
  138.  
  139. void identhosts();
  140. void doidenthosts();
  141. struct hostent *gethostbyaddr();
  142. struct servent *getservbyport();
  143. #ifdef MIPS
  144. struct passwd *getpwuid();
  145. #endif
  146.  
  147. #define TERSEFORMAT   "%s@%s:%s  %s@%s:%s\n"
  148. #define TABULARFORMAT "%-9s%-22s%-9s%-9s%-22s%-9s\n"
  149.  
  150. /* rfc931 port */
  151. #define PORT 113
  152.  
  153. /* rfc931 query timeout */
  154. #define TIMEOUT 30
  155.  
  156. /* identhosts status codes */
  157. #define UNKNOWN -1
  158. #define BAD 0
  159. #define GOOD 1
  160.  
  161. #ifndef NOIDENT
  162. #define NOIDENT "/dev/null"
  163. #endif /* ndef NOIDENT */
  164.  
  165. #define NOIDSIZ 512    /* no more than NOIDSIZ hosts to skip rfc931 id for */
  166. #define INBUFSIZ 80
  167. u_long noid[NOIDSIZ];   /* NOIDENT table */
  168. int ni=0;         /* index into noid */
  169. char *noidfile = NULL;
  170.  
  171. #if defined(IRIX) || defined(MIPS)
  172.  
  173. struct nlist nl[] = {
  174. #define N_FILE 0
  175.     { "file" },
  176. #define N_V 1
  177.     { "v" },
  178.     { "" },
  179. };
  180.  
  181. #else 
  182. struct nlist nl[] = {
  183. #define N_FILE 0
  184.     { "_file" },
  185. #define N_NFILE 1
  186.     { "_nfile" },
  187.     { "" },
  188. };
  189. #endif 
  190.  
  191. kvm_t *kd;
  192.  
  193. /* linked list of connections: local and remote ports, and remote id */
  194. struct connection {
  195.         short remote;      /* remote port on query'd machine */
  196.         short local;       /* local port on query'ed machine */
  197.         char *ident;       /* rfc931 ident string, if available */
  198.     char *user;        /* local user ident string */
  199.         struct connection *next;
  200. };
  201.  
  202. /* linked list of hosts, each with a list of connections to it */
  203. struct hostlist {
  204.         long addr;               /* internet address of host */
  205.         struct connection *conn; /* linked list of connections */
  206.         int identstatus;         /* rfc931 identification status? */
  207.         int sock;                /* rfc931 socket */
  208.         struct hostlist *next;
  209. } *hlist = NUL(struct hostlist *);
  210.  
  211. char *progname;
  212. int d;
  213.  
  214. int hostresolv = TRUE;
  215. int servresolv = TRUE;
  216. int servershow = FALSE;
  217. int printheader = FALSE;
  218.  
  219. void getkvm();
  220. char *InAddrToString();
  221. char *PNumToString();
  222.  
  223.  
  224. void usage()
  225. {
  226.     (void)fprintf(stderr, "Usage: %s [-n][-N][-s][-T][-f filename]\n", 
  227.             progname);
  228.     (void)exit(2);
  229. }
  230.  
  231. struct file *filetable;
  232. int nfiles;
  233.  
  234. int ulongcmp(i, j)
  235. u_long *i, *j;
  236. {
  237.     return(*i-*j);
  238. }
  239.  
  240. /*
  241.  * main - parse arguments and handle options
  242.  */
  243. main(argc, argv)
  244. int argc;
  245. char *argv[];
  246. {
  247.     int c, i;
  248.     int errflg = 0;
  249.     extern int optind;
  250.     extern char *optarg;
  251.     long addr;
  252.     char *localhost = NULL;
  253.     u_long localaddr;
  254.     char *format = TERSEFORMAT;
  255.  
  256. #if defined(IRIX) || defined (MIPS)
  257.     struct var v;
  258.     int socket_index;
  259.  
  260.     socket_index = sysfs(GETFSIND, FSID_SOCKET);
  261. #endif
  262.  
  263.     progname = argv[0];
  264.     while ((c = getopt(argc, argv, "dnNsTf:")) != EOF)
  265.         switch (c) {
  266.         case 'd':
  267.             ++d;
  268.             break;
  269.         case 'n':
  270.             hostresolv = FALSE;
  271.             break;
  272.         case 'N':
  273.             servresolv = FALSE;
  274.             break;
  275.         case 's':
  276.             servershow = TRUE;
  277.             break;
  278.         case 'T':
  279.             format=TABULARFORMAT;
  280.             printheader = TRUE;
  281.             break;
  282.         case 'f':
  283.             noidfile = optarg;
  284.             break;
  285.         default:
  286.             errflg++;
  287.             break;
  288.         }
  289.     if (errflg) {
  290.         usage();
  291.     }
  292.  
  293.     /* read in the hosts not to rfc931-query */
  294.     {
  295.         FILE *fp;
  296.         char buf[INBUFSIZ];
  297.  
  298.         if(NULL==noidfile) noidfile=NOIDENT;
  299.         fp=efopen(noidfile, "r");
  300.         while(NULL!=fgets(buf, INBUFSIZ, fp)){
  301.             struct hostent *h;
  302.             if(buf[0]) buf[strlen(buf)-1]='\0'; else continue; 
  303.             if('#'==buf[0]) continue; /* skip comments */
  304.             if(-1==(noid[ni]=inet_addr(buf))){
  305.                 if(NULL==(h=gethostbyname(buf))){
  306.                     Warning("line %d: unknown host %s",
  307.                         ni+1, buf);
  308.                     ni--;
  309.                 } else {
  310.                     bcopy(h->h_addr_list[0], 
  311.                         (char *)&noid[ni], h->h_length);
  312.                 }
  313.             } 
  314.             ni++;
  315. #ifdef DEBUG
  316.             if(ni){
  317.                 struct in_addr debuga;
  318.                 debuga.s_addr=noid[ni-1];
  319.                 dfprintf(1, stderr, "noid[%d] is %s\n", ni-1, 
  320.                     inet_ntoa(debuga));
  321.             }
  322. #endif /* DEBUG */
  323.         }
  324.         efclose(fp);
  325.     }
  326.     qsort((char *)noid, ni, sizeof(u_long), ulongcmp);
  327.     if(NULL==(kd=kvm_open(NULL, NULL, NULL, O_RDONLY, progname))){
  328.         Error("kvm_open");
  329.     }
  330.     if(0!=kvm_nlist(kd, nl)){
  331.         Error("kvm_nlist: %d", kvm_nlist(kd, nl));
  332.     }
  333. #if defined(IRIX) || defined(MIPS)
  334.     getkvm(nl[N_V].n_value, &v, sizeof(v));
  335.     nfiles=v.v_file;
  336.     addr=nl[N_FILE].n_value;
  337. #else
  338.     getkvm(nl[N_NFILE].n_value, &nfiles, sizeof(nfiles));
  339.     getkvm(nl[N_FILE].n_value, &addr, sizeof(addr));
  340. #endif
  341.     filetable=(struct file *)calloc(nfiles, sizeof(struct file));
  342.     if(NULL==filetable){
  343.         Error("Can't allocate memory for file table.");
  344.     }
  345.     getkvm(addr, filetable, nfiles * sizeof(struct file));
  346.     if(printheader){
  347.         printf(TABULARFORMAT, "LOCAL", "LOCAL", "LOCAL",
  348.             "REMOTE", "REMOTE", "REMOTE");
  349.         printf(TABULARFORMAT, "USER", "HOST", "PORT",
  350.             " USER", " HOST", " PORT");
  351.         printf(TABULARFORMAT, "-----", "-----", "-----",
  352.             "------", "------", "------");
  353.     }
  354.     for(i=0;i<nfiles;i++){
  355.         struct socket so, *sp;
  356.         struct protosw ps;
  357.         struct domain dom;
  358.         struct inpcb pcb;
  359. #ifdef IRIX
  360.         struct inode inode;
  361. #else
  362.         struct ucred cred;
  363. #endif
  364.         struct passwd *pw;
  365.         struct connection *cn;
  366.         struct hostlist *hl;
  367.         int found;
  368.  
  369.         sp=&so;
  370. #ifdef IRIX
  371.         /* for IRIX, need to check for socket after getting inode */
  372.         if(0!=filetable[i].f_count){
  373. #else
  374.         if(filetable[i].f_type == DTYPE_SOCKET &&
  375.            0!=filetable[i].f_count){
  376. #endif
  377.             int uid;
  378.  
  379.             /* grab the socket out of the kernel. Is it
  380.              * a stream socket? If not, go on. */
  381. #ifdef IRIX
  382.             getkvm(filetable[i].f_inode, &inode, sizeof(inode));
  383.             if(socket_index != inode.i_fstyp) continue;
  384.  
  385.             getkvm(soc_fsptr(&inode), &so, sizeof(struct socket));
  386. #else
  387.             getkvm(filetable[i].f_data, sp, sizeof(struct socket));
  388. #endif
  389.             if(sp->so_type!=SOCK_STREAM) continue;
  390.             /* now get the protosw. */
  391.             getkvm(sp->so_proto, &ps, sizeof(ps));
  392.             /* ... and its domain. go on if not internet. */
  393.             getkvm(ps.pr_domain, &dom, sizeof(dom));
  394.             if(dom.dom_family!=AF_INET) continue;
  395.             /* If we've gotten this far, it's a tcp socket. Let's 
  396.              * get the protocol control block... */
  397.             if(0==sp->so_pcb) continue;
  398.             getkvm(sp->so_pcb, &pcb, sizeof(pcb));
  399.             /* (but if foreign addr is ANY, this is a server, so
  400.              *  if we don't want servers, stop here.) */
  401.             if(!servershow){
  402.                 if(INADDR_ANY == pcb.inp_faddr.s_addr) continue;
  403.             }
  404.             /* ...and the user id */
  405. #ifdef IRIX
  406.             uid=inode.i_uid;
  407. #else /* IRIX */
  408.             getkvm(filetable[i].f_cred, &cred, sizeof(cred));
  409.             uid=cred.cr_ruid;
  410. #endif /* IRIX */
  411.             pw=getpwuid(uid);
  412.             if(NULL==pw){
  413.                 pw=new(struct passwd);
  414.                 pw->pw_name=mem(10); 
  415.                 sprintf(pw->pw_name, "#%d", uid);
  416.                 pw->pw_uid=uid;
  417.             }
  418. #ifdef DEBUG
  419.             dfprintf(3, stderr, "user id is %d and user is %s\n", 
  420.                  uid, pw->pw_name);
  421. #endif /* DEBUG */
  422.  
  423.             /* is this host already on the hostlist? */
  424.             found = FALSE;
  425.             for(hl=hlist;NULL!=hl;hl=hl->next){
  426.                 if(pcb.inp_faddr.s_addr == hl->addr){
  427.                     /* found it */
  428.                     found = TRUE; 
  429.                     break;
  430.                 }
  431.             }
  432.             if(!found){
  433.                 /* create new hostlist entry */
  434.                 hl=new(struct hostlist);
  435.                 hl->addr = pcb.inp_faddr.s_addr;
  436.                 hl->conn = NUL(struct connection *);
  437.                 hl->next = hlist;
  438.                 hlist = hl;
  439.             }
  440.             /* create new connection entry */
  441.             cn=new(struct connection);
  442.             cn->remote=pcb.inp_fport;
  443.             cn->local=pcb.inp_lport;
  444.             cn->user=s(pw->pw_name);
  445.             cn->next=hl->conn;
  446.             hl->conn=cn;
  447.             if(NULL==localhost) {
  448.                 /* 
  449.                  * assumption: localhost will always be the
  450.                  * same for all connections. Is this wise? 
  451.                  */
  452.                 localaddr=pcb.inp_laddr.s_addr;
  453.                 localhost=s(InAddrToString(localaddr));
  454.             }
  455.         }
  456.     }
  457.     identhosts(hlist); /* do rfc931 stuff */
  458.     {
  459.         /* print out results */
  460.  
  461.         struct connection *cn;
  462.         struct hostlist *hl;
  463.         char *remotehost, *remoteport, *localport, *remoteuser;
  464.     
  465.         for(hl=hlist;NULL!=hl;hl=hl->next){
  466.             for(cn=hl->conn;NULL!=cn;cn=cn->next){
  467.                 /* for each connection, print line */
  468.  
  469.                 /* localhost constant for all, already known */
  470.                 remotehost=s(InAddrToString(hl->addr));
  471.                 localport=s(PNumToString(cn->local,
  472.                            localaddr));
  473.                 remoteport=s(PNumToString(cn->remote,
  474.                         hl->addr));
  475.                 remoteuser=cn->ident;
  476.                 if(NULL==remoteuser) remoteuser="?";
  477.                 printf(format, cn->user, localhost,
  478.                     localport, remoteuser,
  479.                     remotehost, remoteport);
  480.             }
  481.         }
  482.     }
  483.     exit(0);
  484. }
  485.  
  486. /*
  487.  * InAddrToString(): Convert internet address to a.b.c.d numeric string or 
  488.  *                   hostname. If hostresolv, try to find internet address,
  489.  *                   otherwise use numeric form only. 
  490.  */
  491. char *InAddrToString(iaddr)
  492. u_long iaddr;
  493. {
  494.     static char iaddrbuff[16];
  495.     struct hostent *h;
  496.  
  497.     if(INADDR_ANY == iaddr){
  498.         sprintf(iaddrbuff, "*");
  499.     } else {
  500.         if(hostresolv){
  501.             h=gethostbyaddr((char *)&iaddr, 
  502.                 sizeof(iaddr), AF_INET);
  503.             if(h) return (h->h_name);
  504.         }
  505.         sprintf(iaddrbuff, "%u.%u.%u.%u", (iaddr>>24)&0xff,
  506.         (iaddr>>16)&0xff, (iaddr>>8)&0xff,
  507.         iaddr&0xff);
  508.     }
  509.     return(iaddrbuff);
  510. }
  511.  
  512. /* 
  513.  * PNumToString(): Convert port numbers to string. Successive calls overwrite.
  514.  *                 "tcp" assumed. Resolve to service name if servresolv.
  515.  */
  516. char *PNumToString(pnum, host)
  517. u_short pnum;
  518. u_long host;
  519. {
  520.     static char pnumbuff[16]; 
  521.     struct servent *sv;
  522.  
  523.     if(PORT_ANY == pnum){
  524.         sprintf(pnumbuff, "*");
  525.     } else {
  526.         if(servresolv){
  527.             sv=getservbyport((int)pnum, "tcp");
  528.             if(sv){
  529.                 return(sv->s_name);
  530.             } 
  531.         }
  532.         sprintf(pnumbuff, "%u", (unsigned int)pnum);
  533.     }
  534.     return(pnumbuff);
  535. }
  536.  
  537. /* 
  538.  * getkvm(): get something from kernel memory. Assumes "kd" is a valid
  539.  *           descriptor from kvm_open.
  540.  */
  541. void getkvm(addr, buf, len)
  542. long addr;
  543. char *buf;
  544. int len;
  545. {
  546.     /* get something from kernel memory */
  547.     if(0>kvm_read(kd, addr, buf, len)){
  548.         Error("getkvm: kvm_read(%08x, %d)", addr, len);
  549.     }
  550. }
  551.  
  552. /* 
  553.  * identhosts(): use the rfc931 service, where available, to fill in ident 
  554.  *               field of the connection lists in hostlist. This takes 
  555.  *               advantage, where available, of daemons (eg. pidentd) that can
  556.  *               answer several queries per connection, which is a performance
  557.  *               win, although not strictly rfc931 compliant. 
  558.  *
  559.  *               Restrictions: long identstrings may be lost, since identhosts
  560.  *                             is lazy and only does one read. 
  561.  */
  562. void identhosts(hosts)
  563. struct hostlist *hosts;
  564. {
  565.     int numdescs; 
  566.     int num = 0;
  567.     struct hostlist *ih, *sav, *undone;
  568.  
  569.     numdescs = getdtablesize() - 10;
  570.     ih=undone=hosts;
  571.  
  572.     /* do identhosts in batches of MAX_FILE_DESCRIPTORS - 10, to 
  573.            eliminate risk of running out of file descriptors. */
  574.  
  575.     while(NULL!=ih){
  576.         for(;NULL!=ih && num<numdescs;ih=ih->next){
  577.             if(NULL==bsearch((char *)&(ih->addr), (char *)noid, ni,
  578.                     sizeof(u_long), ulongcmp)){
  579.                 ih->identstatus = UNKNOWN;
  580.                 num++;
  581.             } else {
  582.                 ih->identstatus = BAD;
  583.             }
  584.         }
  585.         if(NULL!=ih){
  586.             sav=ih->next;
  587.             ih->next=NULL;
  588.             doidenthosts(undone);
  589.             num=0;
  590.             ih->next=undone=sav;
  591.         }
  592.     }
  593.     doidenthosts(undone);
  594. }
  595.  
  596. void doidenthosts(hosts)
  597. struct hostlist *hosts;
  598. {
  599.     long maxfd;
  600.     struct connection *ic;
  601.     struct hostlist *ih;
  602.     int r;
  603.     int port=htons(PORT);
  604.     fd_set wds;
  605.     int num=0, decided=0;
  606.     struct timeval timeout;
  607. #ifdef DEBUG
  608.     struct in_addr debuga;
  609. #endif
  610.  
  611.     if(0>(maxfd=ulimit(4,0l))){
  612.         perror("ulimit");
  613.         exit(2);
  614.     }
  615.     
  616.     /* waltz through the connections in the hostlist, setting idents  */
  617.     /* to NULL */
  618.  
  619.     for(ih=hosts;NULL!=ih;ih=ih->next){
  620.         for(ic=ih->conn;NULL!=ic;ic=ic->next){
  621.             ic->ident = NULL; 
  622.         }
  623.         if(UNKNOWN==ih->identstatus) num++;
  624.     }
  625. #ifdef DEBUG
  626.     dfprintf(1, stderr, "Doidenthosts: num=%d\n", num);
  627. #endif
  628.  
  629.     /* get sockets */
  630.     for(ih=hosts;NULL!=ih;ih=ih->next){
  631.         if(ih->identstatus==BAD) continue; /* skip known-BAD hosts */
  632.         if(0>(ih->sock=socket(AF_INET,SOCK_STREAM, 0))){
  633.             perror("socket");
  634.         }
  635.         /* turn on non-blocking I/O */
  636.         (void)fcntl(ih->sock, F_SETFL, FNDELAY);
  637.     }
  638.  
  639.     /* start connects on all of them */
  640.     for(ih=hosts;NULL!=ih;ih=ih->next){
  641.         struct sockaddr_in sa;
  642.     
  643.         if(BAD==ih->identstatus){
  644.             /* 
  645.              * this host has been found on the noident list. Don't
  646.              * bother with connect. 
  647.              */
  648. #ifdef DEBUG
  649.             debuga.s_addr = ih->addr;
  650.             dfprintf(2, stderr, "Skipping %s\n",
  651.                     inet_ntoa(debuga));
  652. #endif /* DEBUG */
  653.         } else {
  654.             sa.sin_family = AF_INET;
  655.             sa.sin_port = port;
  656.             sa.sin_addr.s_addr = ih->addr;
  657.  
  658.             (void)connect(ih->sock, &sa, sizeof(sa));
  659.         }
  660.     }
  661.  
  662.     while(decided<num){
  663.         timeout.tv_sec = (long)TIMEOUT;
  664.         timeout.tv_usec = 0;
  665.  
  666.         /* make write descriptors list for a select, to  */
  667.         /* determine when the connects finish.*/
  668.         FD_ZERO(&wds);
  669.         for(ih=hosts;NULL!=ih;ih=ih->next){
  670.             if(UNKNOWN==ih->identstatus){
  671.                 FD_SET(ih->sock,&wds);
  672.             }
  673.         }
  674.  
  675.         if(0>(r=select((int)maxfd, NUL(fd_set *), &wds, NUL(fd_set *), 
  676.             &timeout))){
  677.             perror("select");
  678.             exit(2);
  679.         }
  680.  
  681.         if(r==0){
  682.             /* we've timed out. Write off all remaining sockets */
  683.             for(ih=hosts;NULL!=ih;ih=ih->next){
  684.                 if(UNKNOWN==ih->identstatus){
  685.                     ih->identstatus = BAD;
  686.                 }
  687.             }
  688.             break;
  689.         }
  690.  
  691.         for(ih=hosts;NULL!=ih;ih=ih->next){
  692.             if(UNKNOWN==ih->identstatus){
  693.                 if(FD_ISSET(ih->sock,&wds)){
  694.                     /* connection has completed */
  695.                     struct sockaddr_in sa;
  696.  
  697.                     sa.sin_family = AF_INET;
  698.                     sa.sin_port = port;
  699.                     sa.sin_addr.s_addr = ih->addr;
  700.  
  701.                     if(-1==connect(ih->sock, &sa, 
  702.                             sizeof(sa))){
  703.                         if(EISCONN==errno){
  704.                             ih->identstatus=GOOD;
  705.                         } else {
  706.                             ih->identstatus=BAD;
  707.                         }
  708.                         decided++;
  709.                     }
  710.                     r--;
  711.                     if(r<1) break;
  712.                 }
  713.             }
  714.         }
  715. #ifdef DEBUG
  716.         dfprintf(2, stderr, "%d of %d decided\n", decided, num);
  717. #endif /* DEBUG */
  718.     }
  719.     
  720. #ifdef DEBUG
  721.     dfprintf(2, stderr, "Results:\n");
  722. #endif
  723.     for(ih=hosts;NULL!=ih;ih=ih->next){
  724.         /* turn off nonblocking I/O */
  725.         if(GOOD==ih->identstatus) (void)fcntl(ih->sock, F_SETFL, 0); 
  726.  
  727. #ifdef DEBUG
  728.                 debuga.s_addr = ih->addr;
  729.  
  730.         switch(ih->identstatus){
  731.         case GOOD:
  732.             dfprintf(2, stderr, "%s good\n",inet_ntoa(debuga));
  733.             break;
  734.         case BAD:
  735.             dfprintf(2, stderr, "%s bad\n",inet_ntoa(debuga));
  736.             break;
  737.         case UNKNOWN:
  738.                         dfprintf(2, stderr, "%s unknown\n",inet_ntoa(debuga));
  739.             break;
  740.         default:
  741.             dfprintf(2, stderr, "%s strange status %d\n",
  742.                 inet_ntoa(debuga), ih->identstatus);
  743.             break;    
  744.         } 
  745. #endif
  746.     }
  747.  
  748.     /* now, for each ok host, send an appropriate request off to the 
  749.            rfc931 server */
  750.     for(ih=hosts;NULL!=ih;ih=ih->next){
  751.         char ubuff[1024], osbuff[128];
  752.         int remote, local;
  753.         for(ic=ih->conn;NULL!=ic;ic=ic->next){
  754.             char wbuff[80];
  755.             char rbuff[1024];
  756.             int n;
  757.  
  758.             if(GOOD!=ih->identstatus) continue;
  759.  
  760.             (void)sprintf(wbuff, "%d,%d\n", ic->remote, ic->local);
  761.  
  762.             if(0>=write(ih->sock, wbuff, strlen(wbuff))){
  763.             }
  764.             if(0>(n=read(ih->sock, rbuff, sizeof(rbuff)))){
  765.                 (void)close(ih->sock);
  766.                 ih->identstatus=BAD;
  767.                 break;
  768.             } else if(0==n){
  769.                 /* other side may have closed conn, reopen */
  770.                                 /* and try again. */
  771.                 struct sockaddr_in sa;
  772. #ifdef DEBUG
  773.                 dfprintf(3, stderr, "reopening\n");
  774. #endif
  775.  
  776.                 /* close the socket */
  777.                 (void)close(ih->sock);
  778.  
  779.                 /* get a new socket */
  780.                 if(0>(ih->sock=socket(AF_INET,
  781.                     SOCK_STREAM, 0))){
  782.                     ih->identstatus=BAD;
  783.                     break;
  784.                 }
  785.  
  786.                 /* connect */
  787.                 sa.sin_family = AF_INET;
  788.                             sa.sin_port = port;
  789.                             sa.sin_addr.s_addr = ih->addr;
  790.                 if(0>connect(ih->sock,&sa,
  791.                     sizeof(sa))){
  792.                     ih->identstatus=BAD;
  793.                     break;
  794.                 }
  795.  
  796.                 if(0>write(ih->sock, wbuff, strlen(wbuff))){
  797.                     (void)close(ih->sock);
  798.                     ih->identstatus=BAD;
  799.                     break;
  800.                 }
  801.                 if(0>(n=read(ih->sock, rbuff, sizeof(rbuff)))){
  802.                     (void)close(ih->sock);
  803.                     ih->identstatus=BAD;
  804.                     break;
  805.                 }
  806.                 /* 
  807.                  * Kludge alert: 
  808.                  * It's not technically correct to be satisfied
  809.                  * with just one read here; should probably be 
  810.                  * reading until buff overflow or newline.
  811.                  * This'll miss stuff if remote identd returns
  812.                  * too much data.
  813.                  */
  814.                 if(0==n){
  815.                     /* still eof! give up. */
  816.                     (void)close(ih->sock);
  817.                     ih->identstatus=BAD;
  818.                     break;
  819.                 }
  820.             } 
  821.             rbuff[n]='\0';
  822. #ifdef DEBUG
  823.             dfprintf(1, stderr, "%lu: %s", ih->addr, rbuff);
  824. #endif
  825.             /* Check the response */
  826.             if(4>sscanf(rbuff, "%d , %d : USERID : %s : %s", 
  827.                   &remote, &local, osbuff, ubuff)){
  828.                 /* bad response. */
  829. #ifdef DEBUG
  830.                 dfprintf(2, stderr, "bad response %s\n", rbuff);
  831. #endif
  832.                 continue;
  833.             }
  834.             if(ic->local!=local || ic->remote!=remote){
  835.                 /* Port number mismatch. */
  836. #ifdef DEBUG
  837.                 dfprintf(2, stderr, 
  838.                   "port mismatch %s: %d, %d with %d, %d\n",
  839.                      rbuff, remote, local, ic->remote, 
  840.                    ic->local);
  841. #endif
  842.                 continue;
  843.             }
  844.  
  845.             /* It's okay. Trash trailing newline or lf, set ident 
  846.                field */
  847.             {
  848.                 int sl=strlen(ubuff)-1;
  849.                 if(ubuff[sl]=='\n' || ubuff[sl]=='\r'){
  850.                     ubuff[sl]='\0';
  851.                 } 
  852.             } 
  853.             ic->ident = s(ubuff);
  854. #ifdef DEBUG
  855.             dfprintf(2, stderr, 
  856.                 "Host %lu remote %d local %d user %s\n",
  857.                 ih->addr, ic->remote, ic->local, ic->ident);
  858. #endif
  859.         }
  860.         /* we're done with this host */
  861.         (void)close(ih->sock);
  862.     }
  863. }
  864.